---
permalink: "/2011/3/21/Foundations-of-Programming-2-Appendix-B-Adv-jQuery/"
layout: post
date: 2011-03-21
title: "Foundations of Programming 2 - Appendix B - Advanced jQuery"
tags: [ebook, ui]
---
<h2>Appendix B - Advanced jQuery</h2>

<p>jQuery is a rewarding library to learn, not only because what you learn in the first 30 minutes can be of real use, but also because the foundations you learn early on serves as the backbone for more advance usage. This chapter is dedicated to a few of those more advanced jQuery usages.</p>

<h3>Custom Selectors</h3>

<p>Our story, thus far, has been that jQuery uses CSS selectors (including some of the more advanced CSS3 selectors). While this is true, jQuery also has its own selectors which are quite handy. These custom selectors are easy to spot as they always begin with a colon. We already saw one in our final example of the previous chapter, <code>:first</code>. There's also a corresponding <code>:last</code> selector. Beyond these, and a handful of others there are a few which are worth pointing out.</p>

<h4>:not</h4>

<p>The <code>:not</code> selector looks for the inverse of the supplied selector. You use it like so:</p>

{% highlight javascript %}var $notFirst = $('.tabs a').filter(':not(:first)');
//or
var $notFirst = $('.tabs :not(a:first)');
{% endhighlight %}

<p>As you can see <code>:not</code> is special, though not unique, as it takes a sort of argument to work.</p>

<h4>:eq</h4>

<p>The <code>:eq</code> selector, like <code>:not</code>, takes an argument which is the index of the element we want to find:</p>

{% highlight javascript %}//yes, it's base zero
var $secondLink = $('a:eq(1)');
{% endhighlight %}

<p>It isn't too uncommon that you'll see and use this ugly code:</p>

{% highlight javascript %}var $item = $('td:eq(' + i + ')');
{% endhighlight %}

<p>Where i would be the index of a cell you want to find.</p>

<h4>:visible and :hidden</h4>

<p><code>:visible</code> and <code>:hidden</code> aren't particularly advanced, but they are useful enough to point out. They work like you think, and they work surprisingly well. They both go beyond simply checking the CSS visibility of an item, but also its height and all types of things which really indicate whether the item is visible or not. In our tabs example from the previous chapter, instead of potentially hiding already hidden panels, we could have done:</p>

{% highlight javascript %}//only hide the visible panels
$panels.filter(':visible').hide();
{% endhighlight %}

<p>Though, to be honest, I think it's simpler just to hide them all.</p>

<h4>Writing Your Own</h4>

<p>Occasionally you might need to write your own selector. This doesn't happen often, but on a few occasions I've had need to find elements by their <code>innerText</code> value (there's a <code>:contains</code> selector, but that does a fuzzy search, I wanted an exact one). jQuery makes this easy:</p>

{% highlight javascript %}$.expr[':'].textIs = function(obj, index, meta, stack){
        return obj.innerText == meta[3];
};
{% endhighlight %}

<p>We attach our own function to jQuery's <code>$.expr[:]</code> object named <code>textIs</code>. The first parameter is the DOM element being compared (we could convert that to a jQuery object if we needed to). The second parameter is the index of this particular element with respect to all potential matches. The meta parameter is information about the actual selector - you can think of it as the captures from a regular expression, and you'll probably need to <code>console.log(meta)</code> to get a sense for what part you are interested in. The last parameter is an array of all the potentially found elements (so <code>stack[index] == obj</code>) - this is useful if your selector is doing something relative to other elements.</p>

<h4>Hierarchy Flattening</h4>

<p>There's at least one thing which isn't intuitive about how selectors work (well, to me at least). Despite the fact that the DOM is a hierarchy, the <code>jQuery</code> method always flattens results. What do I mean by this? Say we wanted to add a class to the last column of every row. You might be tempted to try:</p>

{% highlight javascript %}$('tbody tr').children('td:last').addClass('last');
{% endhighlight %}

<p>But if your table has more than 1 row, this isn't going to work. When you call <code>children</code>, the cells aren't grouped by rows, they are flattened into a single array. Therefore, the above code is only going to apply the <code>last</code> class to the very last cell of the entire table. The solution is to use the <code>each</code> method:</p>

{% highlight javascript %}$('tbody tr').each(function(index, tr)
{
    $(tr).children(':last').addClass('last');
});
{% endhighlight %}

<p>The <code>each</code> method, along with events, is where you are most likely to turn a DOM element into a jQuery object.</p>

<h4>DOM Creation</h4>

<p>We've seen how we can use the <code>jQuery</code> method to get a jQuery object form a CSS selector or from an existing DOM element. The method actually has a third usage: creating new elements. If you pass <code>$</code> an HTML tag, it'll create the corresponding element:</p>

{% highlight javascript %}var $div = $('<div>');
{% endhighlight %}

<p>You can even get fancy:</p>

{% highlight javascript %}var $div = $('<div class="neato">neat!</div>');
//or
var $div = $('<div>').addClass('neato').text('neat!');
{% endhighlight %}

<p>Note that an element created this way isn't added to the DOM by default - only you know where it ought to go:</p>

{% highlight javascript %}var $div = $('<div class="error">').appendTo($('#header'));
{% endhighlight %}

<h3>AJAX</h3>

<p>jQuery has a handful of methods which makes writing ajax code simple. The first two are <code>$.get</code> and <code>$.post</code> which, as you can probably guess, issue an ajax <code>get</code> and <code>post</code> respectively. They both take 4 parameters: the URL, the data, a callback, and a return type:</p>

{% highlight javascript %}$.get('/user', {id: 123}, function(response, status)
{
    //do something
}, 'json');

$.post('/game', {name: 'TileFlod', version: 2}, gameSaved, 'html');
function gameSaved(response, status)
{
    //do something
}
{% endhighlight %}

<p>This is probably a good time to point out that given a form object, you can call <code>serialize</code> on it to get submittable data:</p>

{% highlight javascript %}$.post('/game', $('#addGame').serialize(), ..., ...);
{% endhighlight %}

<p>Both of the above methods actually abstract the more powerful <code>$.ajax</code> method. This method takes a single object literal:</p>

{% highlight javascript %}$.ajax(
{
    async: false,
    cache: false,
    url: '/game',
    type: 'get',
    success: function(response, status) {

    },
    //and so on
});
{% endhighlight %}

<p>Finally, the last ajax method that we'll look at is the <code>load</code> plugin. <code>load</code> abstracts <code>$.get</code> or <code>$.post</code> (depending on whether data is provided), and automatically loads the response into the calling object:</p>

{% highlight javascript %}$('#myDiv').load('/about/moreinfo');
{% endhighlight %}

<p>Which is equivalent to:</p>

{% highlight javascript %}$.get('/about/moreinfo', null, function(response)
{
    $('myDiv').html(response);
}, 'html');
{% endhighlight %}

<h3>Delegates</h3>

<p>Sooner or later you're going to want to sprinkle your jQuery goodness over dynamically loaded HTML. The most common example is a list of rows, with a click event (to see the details) which dynamically grows/changes. An ugly solution, which might be quite tricky at times, is to hook events as your new rows come in.</p>

{% highlight javascript %}//called initially for the statically loaded rows
var $list = $('#myList');
$list.find('tr').click(showDetails);

function showDetails(row)
{
    //do something
}

//somewhere else in your code:
$('#add').click(function()
{
    $.post(URL, PARAMS, function(newRow)
    {
        $(newRow).click(showDetails).appendTo($list.find('tbody'));
    });
});
{% endhighlight %}

<p>You aren't only repeating the code to set up the click, but you're also polluting the save process with details about the table's display. There's yet another problem with the above approach - it doesn't scale. I know talking about scalability with respect to "client-side" code might seem odd, but add enough event handlers and you really might start having performance issues.</p>

<p>The solution? jQuery has a <code>delegate</code> method which works by attaching a single event to a parent element which then watches (or delegates) to child elements. Let's rewrite the above code to use it:</p>

{% highlight javascript %}var $list = $('#myList');
$list.delegate('tr', 'click', showDetails);

function showDetails(row)
{
    //do something
}
{% endhighlight %}

<p>That's it. Rows which are dynamically added later are automatically covered by the delegate since it exists on their (future) parent table. The code says that whenever a <code>tr</code> element within <code>#myList</code> is <code>click</code>ed execute <code>showDetails</code>. The selector, the first parameter, can be any jQuery selector. The implementation relies on bubbling, which does mean that for some events it won't work, but for the most common (like <code>click</code>) it's an extremely powerful solution.</p>

<p>Its worth pointing out that jQuery also has a <code>live</code> method, which is very similar to <code>delegate</code>:</p>

{% highlight javascript %}$('tr').live('click', showDetails);

function showDetails(row)
{
    //do something
}
{% endhighlight %}

<p>While the syntax for <code>live</code> is nicer, it lacks a scope that <code>delegate</code> has. This results in poorer performance, and also less control (you might not want to apply this to <strong>every</strong> <code>tr</code> on the page).</p>

<h3>Writing Plugins</h3>

<p>So far we've looked at a number of built-in jQuery methods which, while useful on their own, truly shine when used within plugins. I like to think of plugins as belonging to one of two categories. The first category is for UI plugins, like tabs or dialogs. The second is for more task specific plugins, often bringing multiple UI plugins together to accomplish something pretty specific within a page/app. Both are built the exact same way, the only difference is that you want to make sure UI plugins are truly reusable. (It's worth noting that there's a large number of existing quality and free UI plugins available for jQuery, just google for them).</p>

<p>Whenever I write a plugin I start with a basic template. At first, parts of the template might seem difficult. We'll go over those difficult parts, but even if you don't fully understand it now, you can easily use it and safely ignore the plumbing. First though, when you call a method on a jQuery object, be it a built-in method or a plugin, that method exists within the the <code>jQuery.fn</code> object. So, a basic example might look something like:</p>

{% highlight javascript %}//remember $ and jQuery are the same thing
$.fn.tabs = function()
{
        //do something
};
{% endhighlight %}

<p>Since it's possible for another library to define <code>$</code> (this was a common problem before jQuery become overwhelmingly popular), there's a safer way to write the same code:</p>

{% highlight javascript %}(function($))
{
    $.fn.tabs = function()
    {

    };
})(jQuery);
{% endhighlight %}

<p>I know it looks a little crazy, but it's quite neat and worth understanding. There's nothing more complicated here than defining a dynamic method and passing in a parameter. Look at a more explicit example:</p>

{% highlight javascript %}(function(question, answer)
{
    alert(question + ' ' + answer);
})('its over', 9000)();
{% endhighlight %}

<p>We define a method that takes 2 parameters, <code>question</code> and <code>answer</code>, and then invoke it with with two values <code>its over</code> and <code>9000</code>.</p>

<p>The jQuery example is the same, except it's a single parameter which we name <code>$</code> and we pass in the <code>jQuery</code> object. The effect is that even if <code>$</code> is defined as something else globally, within our dynamic method (technically a closure), it's simply a parameter which we've assigned to <code>jQuery</code> (another library could always come along and redefine <code>jQuery</code>, but that's less likely).</p>

<p>The other thing that's important to remember when writing a plugin is that, like most built-in jQuery methods, you should write your plugin so that it both works on an array of jQuery objects and so that it returns the jQuery object (this allows your plugin to be used in a method chain).</p>

<p>With that out of the way our template (which follows the two rules we've discussed above) looks like:</p>

{% highlight javascript %}(function($)
{
  var defaults = {};
  $.fn.PLUGINNAME = function(options)
  {
    var opts = $.extend({}, defaults, options);
    return this.each(function()
    {
      if (this.PLUGINNAME) { return false; }
      var self =
      {
        initialize: function()
        {

        }
      };
      this.PLUGINNAME = self;
      self.initialize();
    });
  };
})(jQuery);
{% endhighlight %}

<p>All you need to do for your own plugin is copy the above and replace the three instances of <code>PLUGINNAME</code> with the name of your own plugin. The <code>defaults</code> variable is used for your plugins default values - which can be overwritten by supplying a options when creating your plugin. Notice the call <code>return this.each</code>, this is the magic that makes our plugin work against and array of jQuery object and which returns that array. We also store our plugin within the item by calling <code>this.PLUGINNAME = self</code> and when we setup the plugin, we return if <code>this.PLUGINNAME</code> is not null - this makes sure that, for a given element, our plugin is only defined once.</p>

<p>Let's use this template to rewrite our <code>tabs</code> code from the previous chapter to get a better idea of how this all actually works:</p>

{% highlight javascript %}<style>
    a.active{font-weight:bold;}
</style>

<ul class="tabs">
    <li><a href="#main">main</a></li>
    <li><a href="#about">about</a></li>
    <li><a href="#download">download</a></li>
</ul>
<div id="mainPanels">
    <div id="main" class="panel">This is the main panel</div>
    <div id="about" class="panel">this is the about panel</div>
    <div id="download" class="panel">this is the download panel</div>
</div>

(function($)
{
  var defaults = {initialTab: ':first', panelContainer: null};
  $.fn.tabs = function(options)
  {

        //overwrites the detauls with the supplied options
    var opts = $.extend({}, defaults, options);

        return this.each(function()
        {
      if (this.tabs) { return false; }

            //we can define fields for our plugin to have access to
            var $tabContainer = $(this);
            var $links = $tabContainer.find('a');
            var $panelContainer = $(opts.panelContainer);

        var self =
        {
        initialize: function()
        {
                    self.hidePanels();
            $links.click(self.clicked).filter(opts.initialTab).click();
        },
                clicked: function()
                {
                    var $link = $(this);
                    $links.removeClass('active');
                    $link.addClass('active');
                    self.hidePanels();
                    $panelContainer.find($link.attr('href')).show();
                },
                hidePanels: function()
                {
                    $panelContainer.find('.panel').hide();
                }
      };
      this.tabs = self;
      self.initialize();
    });
  };
})(jQuery);
{% endhighlight %}

<p>We've made some slight modifications to make the plugin more reusable, but for the most part not much has changed. We can call this plugin by simply doing:</p>

{% highlight javascript %}$('.tabs').tabs({panelContainer: '#mainPanels'});
{% endhighlight %}

<p>Most of my plugins follow the same pattern. The real trick, as I mentioned before, is to keep plugins focused. As you start to write plugins, there'll likely be some discomfort about purpose. Each of your page will likely have unique needs and it might be tempting to write a large plugin for each. You can start this way, but try to refactor common code out into their own plugins.</p>

<h3>In This Chapter</h3>

<p>Over the last two chapter's we've seen the power of jQuery. Yet, for all its flexibility, the truth is that jQuery is an extremely simple and focused library. If you understand and remember the fundamentals - how the <code>$</code> can be used, understanding the relationship between a DOM element and its jQuery wrapper, and knowing what <code>this</code> means in a given context - then you'll easily master jQuery. It's a library worth knowing, not just because of how useful it is, but also because it showcases what a good library should be like - focused and simple, yet somehow easily extensible. Don't get overwhelmed though, since we did cover a lot of material. Start small and, at your own pace, move forward.</p>
